package com.agger.harmonyosthemeseditor.abilities;

import com.agger.harmonyosthemeseditor.ResourceTable;
import com.agger.harmonyosthemeseditor.intelligent.FileManager;
import com.agger.harmonyosthemeseditor.intelligent.GeneralUtil;
import com.agger.harmonyosthemeseditor.intelligent.ImageIntelligent;
import com.agger.harmonyosthemeseditor.userinterface.GeneralToast;
import com.agger.harmonyosthemeseditor.userinterface.ResourceTool;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.graphics.Surface;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.agp.window.service.WindowManager;
import ohos.bundle.AbilityInfo;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.camera.CameraKit;
import ohos.media.camera.device.*;
import ohos.media.image.ImageReceiver;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.ImageFormat;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Position;
import ohos.media.image.common.Size;

import java.io.File;

public class TakePhotoAbility extends Ability
{

    private static int DISPLAY_WIDTH;

    private static int DISPLAY_HEIGHT;

    private SurfaceProvider surfaceProvider;

    private ImageReceiver imageReceiver;

    private static boolean isFrontCamera = false;

    private Surface previewSurface;

    private Camera cameraDevice;

    private Component BottomLayout;

    private ComponentContainer surfaceLayout;

    private String outPutName;

    private SurfaceOps.Callback callback;

    private final EventHandler eventHandler = new EventHandler(EventRunner.current()) {};

    private Component Button_TakePhoto,Button_Quit,Button_SwitchCamera;
    private static boolean isProcessing = false;

    @Override
    public void onStart(Intent intent) 
    {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_slice_take_photo);
        getWindow().setTransparent(true);
        setDisplayOrientation(AbilityInfo.DisplayOrientation.PORTRAIT);
        try{getWindow().addFlags(WindowManager.LayoutConfig.MARK_TRANSLUCENT_STATUS|WindowManager.LayoutConfig.MARK_TRANSLUCENT_NAVIGATION|WindowManager.LayoutConfig.MARK_FULL_SCREEN);}catch(Exception ex){ex.printStackTrace();}
        GeneralUtil.setWindowToMaxRefreshRate(getWindow());
        Display display = DisplayManager.getInstance().getDefaultDisplay(this).get();
        DISPLAY_WIDTH = display.getAttributes().width;
        DISPLAY_HEIGHT = display.getAttributes().height;
        if(!isFrontCamera)
        {
            if((((float)DISPLAY_WIDTH)/DISPLAY_HEIGHT)<1f)
                DISPLAY_WIDTH = DISPLAY_HEIGHT;
            else if((((float)DISPLAY_WIDTH)/DISPLAY_HEIGHT)>1f)
                DISPLAY_HEIGHT = DISPLAY_WIDTH;
        }else
        {
            float i = 0.699999955f;
            if((((float)DISPLAY_WIDTH)/DISPLAY_HEIGHT)<i)
                DISPLAY_WIDTH = (int)(DISPLAY_HEIGHT*i);
            else if((((float)DISPLAY_WIDTH)/DISPLAY_HEIGHT)>i)
                DISPLAY_HEIGHT = (int)(DISPLAY_WIDTH/i);
        }
        outPutName = intent.getStringParam("output");
        isProcessing = false;
        initComponents();
        initSurface();
    }

    private void initSurface() 
    {
        surfaceProvider = new SurfaceProvider(this);
        surfaceProvider.setWidth(DISPLAY_WIDTH);
        surfaceProvider.setHeight(DISPLAY_HEIGHT);
        surfaceProvider.pinToZTop(false);
        if (surfaceProvider.getSurfaceOps().isPresent())
        {
            callback = new SurfaceCallBack();
            surfaceProvider.getSurfaceOps().get().addCallback(callback);
        }
        surfaceLayout.addComponent(surfaceProvider);
    }

    private void initComponents()
    {
        BottomLayout = findComponentById(ResourceTable.Id_bottomLayout);
        surfaceLayout = (ComponentContainer)findComponentById(ResourceTable.Id_surfaceLayout);
        Button_TakePhoto = findComponentById(ResourceTable.Id_take_photo_button);
        Button_Quit = findComponentById(ResourceTable.Id_quit_button);
        Button_SwitchCamera = findComponentById(ResourceTable.Id_switch_camera_button);
        Button_Quit.setClickedListener(component->{isFrontCamera=false;terminateAbility();});
        Button_TakePhoto.setClickedListener(this::Capture);
        Button_SwitchCamera.setClickedListener(this::switchCamera);
    }

    private void openCamera() 
    {
        imageReceiver = ImageReceiver.create(DISPLAY_WIDTH,DISPLAY_HEIGHT,ImageFormat.JPEG, 1);
        imageReceiver.setImageArrivalListener(this::saveImage);
        CameraKit cameraKit = CameraKit.getInstance(getApplicationContext());
        String[] cameraList = cameraKit.getCameraIds();
        String cameraId = "";
        for (String mCameraId : cameraList)
        {
            int faceType = cameraKit.getCameraInfo(mCameraId).getFacingType();
            switch (faceType)
            {
                case CameraInfo.FacingType.CAMERA_FACING_FRONT:
                    if (isFrontCamera)
                    {
                        cameraId = mCameraId;
                    }
                    break;
                case CameraInfo.FacingType.CAMERA_FACING_BACK:
                    if (!isFrontCamera)
                    {
                        cameraId = mCameraId;
                    }
                    break;
                case CameraInfo.FacingType.CAMERA_FACING_OTHERS:
                default:
                    break;
            }
        }
        if (cameraId != null && !cameraId.isEmpty()) {
            CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl();
            cameraKit.createCamera(cameraId, cameraStateCallback, eventHandler);
        }
    }

    private void saveImage(ImageReceiver receiver)
    {
        try
        {
            Button_Quit.setEnabled(false);
            Button_TakePhoto.setEnabled(false);
            Button_SwitchCamera.setEnabled(false);
            isProcessing = true;
            eventHandler.postTask(()->new GeneralToast(getContext()).setText(ResourceTool.getString(getContext(),ResourceTable.String_TakePhoto_Saving_Toast,"正在改善画质，请持稳您的设备")).showOnTop(),0);
            File saveFile = new File(outPutName);
            if(saveFile.exists()) FileManager.deleteFile(outPutName);
            ohos.media.image.Image image = receiver.readNextImage();
            ohos.media.image.Image.Component component = image.getComponent(ImageFormat.ComponentType.JPEG);
            if(isFrontCamera)
            {
                ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
                srcOpts.formatHint = "image/jpeg";
                byte[] bytes = new byte[component.remaining()];
                component.read(bytes);
                ImageSource imageSource = ImageSource.create(bytes,srcOpts);
                ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
                PixelMap mPixelMap = imageSource.createPixelmap(decodingOptions);
                Size mSize = mPixelMap.getImageInfo().size;
                int mWidth = mSize.height;
                int mHeight = mSize.width;
                PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
                initializationOptions.size = new Size(mWidth,mHeight);
                initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
                initializationOptions.editable = true;
                PixelMap nPixelMap = PixelMap.create(initializationOptions);
                for(int h = 0; h < mHeight; h++)
                {
                    for(int w = 0; w < mWidth; w++)
                    {
                        nPixelMap.writePixel(new Position(w,h), mPixelMap.readPixel(new Position(mHeight-h-1,w)));
                    }
                }
                ImageIntelligent.saveImage(outPutName,nPixelMap);
            }else
            {
                ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
                srcOpts.formatHint = "image/jpeg";
                byte[] bytes = new byte[component.remaining()];
                component.read(bytes);
                ImageSource imageSource = ImageSource.create(bytes,srcOpts);
                ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
                PixelMap mPixelMap = imageSource.createPixelmap(decodingOptions);
                Size mSize = mPixelMap.getImageInfo().size;
                int mWidth = mSize.height;
                int mHeight = mSize.width;
                PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
                initializationOptions.size = new Size(mWidth,mHeight);
                initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
                initializationOptions.editable = true;
                PixelMap nPixelMap = PixelMap.create(initializationOptions);
                for(int h = 0; h < mHeight; h++)
                {
                    for(int w = 0; w < mWidth; w++)
                    {
                        nPixelMap.writePixel(new Position(w,h), mPixelMap.readPixel(new Position(h,mWidth-w-1)));
                    }
                }
                ImageIntelligent.saveImage(outPutName,nPixelMap);
            }
            isFrontCamera = false;
            setResult(-1,null);
            terminateAbility();
        }catch (Exception e){e.printStackTrace();}
    }

    private void Capture(Component component)
    {
        if (cameraDevice == null || imageReceiver == null) 
        {
            return;
        }
        FrameConfig.Builder framePictureConfigBuilder = cameraDevice.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PICTURE);
        framePictureConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
        FrameConfig pictureFrameConfig = framePictureConfigBuilder.build();
        cameraDevice.triggerSingleCapture(pictureFrameConfig);
    }

    private void switchCamera(Component component) 
    {
        isFrontCamera = !isFrontCamera;
        if (cameraDevice != null)
        {
            cameraDevice.release();
        }
        restart();
    }

    private class CameraStateCallbackImpl extends CameraStateCallback
    {
        CameraStateCallbackImpl() {}

        @Override
        public void onCreated(Camera camera)
        {
            if (surfaceProvider.getSurfaceOps().isPresent())
            {
                previewSurface = surfaceProvider.getSurfaceOps().get().getSurface();
            }
            if (previewSurface == null)
            {
                return;
            }
            CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder();
            cameraConfigBuilder.addSurface(previewSurface);
            cameraConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
            camera.configure(cameraConfigBuilder.build());
            cameraDevice = camera;
            updateComponentVisible(true);
        }

        @Override
        public void onConfigured(Camera camera)
        {
            FrameConfig.Builder framePreviewConfigBuilder = camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
            framePreviewConfigBuilder.addSurface(previewSurface);
            camera.triggerLoopingCapture(framePreviewConfigBuilder.build());
        }
    }

    private void updateComponentVisible(boolean isVisible)
    {
        BottomLayout.setVisibility(isVisible ? Component.VISIBLE : Component.INVISIBLE);
    }

    private class SurfaceCallBack implements SurfaceOps.Callback
    {
        @Override
        public void surfaceCreated(SurfaceOps callbackSurfaceOps)
        {
            if (callbackSurfaceOps != null)
            {
                callbackSurfaceOps.setFixedSize(DISPLAY_WIDTH,DISPLAY_HEIGHT);
            }
            eventHandler.postTask(TakePhotoAbility.this::openCamera,200);
        }

        @Override
        public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) {}

        @Override
        public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) {}
    }

    private void releaseCamera() 
    {
        if (cameraDevice != null) 
        {
            cameraDevice.release();
        }

        if (imageReceiver != null) 
        {
            imageReceiver.release();
        }
    }

    @Override
    protected void onStop() 
    {
        releaseCamera();
    }

    @Override
    protected void onBackPressed()
    {
        if(!isProcessing)
        {
            isFrontCamera=false;
            terminateAbility();
        }
    }
}
